'use strict';

const Command = require('@imooc-my-test1/command');
const Package = require('@imooc-my-test1/package');
const { SpinnerFn, Sleep, Spawn, SpawnAsync } = require('@imooc-my-test1/utils');
const path = require('path');
const userHome =require('user-home')
const ejs =require('ejs');

const log = require('@imooc-my-test1/log');
const {handleGetTemplate} = require('./api.js')

const fs =require('fs');
const inquirer = require('inquirer')
const fse = require('fs-extra')
const semver = require('semver');

const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'

const TEMPLATE_TYPE_NORMAL = 'normal';
const TEMPLATE_TYPE_CUSTOM = 'custom'

const WHITE_COMMAND = ['npm', 'cnpm'];
const nameReg = /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/

class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || '';
    this.force = !!this._argv[1].force;
    log.verbose('projectName', this.projectName)
    log.verbose('force', this.force)
  }
  // 实际执行流程
  async exec() {
    try {
      // 1. 准备阶段
      this.projectInfo = await this.prepare();
      if(!this.projectInfo) return
      log.verbose('projectInfo', this.projectInfo)
      // 2. 下载模版
      await this.downloadTemplate()
      // 3. 安装模版
      await this.installTemplate()
    } catch (error) {
      log.error(error.message)
    }
  }
  
  async installTemplate() {
    if(this.nowDownloadTemplateInfo) {
      if(!this.nowDownloadTemplateInfo.type) {
        this.nowDownloadTemplateInfo.type = TEMPLATE_TYPE_NORMAL
      } 

      log.verbose('nowDownloadTemplateInfo', this.nowDownloadTemplateInfo)

      if(this.nowDownloadTemplateInfo.type === TEMPLATE_TYPE_NORMAL) {
        await this.installNormalTemplate()
      } else if(this.nowDownloadTemplateInfo.type === TEMPLATE_TYPE_CUSTOM) {
        await this.installCustomTemplate()
      } else {
        throw new Error('项目模版信息类型无法识别');
      }
    } else {
      throw new Error('项目模版信息不存在');
    }

  }

  async ejsRender(ignore) {
    const projectInfo = this.projectInfo;
    return new Promise(function(resolve, reject) {
      const { globSync } = require('glob')
      try {
        const files = globSync(['*.js', '*.json', '*.vue'], {
          cwd: process.cwd(),
          ignore: ignore || [],
          nodir: true
        })

        Promise.all(files.map(file => {
          return new Promise((resolve1, reject1) => {
            ejs.renderFile(file, projectInfo, (err, result) => {
              if(err) {
                reject1(err)
              } else {

                resolve1([file,result ])
              }
            })
          })
        })).then((filesRes) => {
          filesRes.map((resArr, index) => {
            const [file ,result] = resArr;
              fse.writeFileSync(file,result)
          })
          resolve()
        })
      } catch (error) {
        reject(error)
      }
      
    })
  }

  async installNormalTemplate() {
    const Spinner = SpinnerFn('正在拷贝模版..')
    try {
      const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template');
      const targetPath = process.cwd();
      fse.ensureDirSync(templatePath)
      fse.ensureDirSync(targetPath)
      fse.copySync(templatePath, targetPath);
    } catch (error) {
      throw error
    } finally {
      Spinner.stop(true);
      log.success('模版安装成功')
    }
    const ignore = ['node_modules/**', 'public/**', 'README.md', '**/*.{png,jpeg,svg,gif}', 'bin/**', 'build/**']
    await this.ejsRender(ignore)

    const {installCommand, startCommand } =  this.nowDownloadTemplateInfo;
    console.log('jafsdjfs', this.nowDownloadTemplateInfo)

   await this.execCommand(installCommand, '依赖安装过程中失败')
   console.log('jafsdjfs')

   await  this.execCommand(startCommand, '依赖启动过程中失败')
  }

  async execCommand(commandTxt, errTxt) {
    console.log('jj')
    if(commandTxt) {
      const commandTxtArr =commandTxt.split(' ')
      if(commandTxtArr.length > 0) {
        const cmd = commandTxtArr[0];
        this.checkCommand(cmd)
        const args = commandTxtArr.slice(1);
        const ret = await SpawnAsync(cmd, args, {
          cwd: process.cwd(),
          stdio: 'inherit',
        })
        if(ret !== 0) {
          throw new Error(errTxt)
        }
      }
    }
  }
  checkCommand(cmd) {
    if(!WHITE_COMMAND.includes(cmd)) {
      throw new Error('配置的命令不可执行')
    }
  }
  async installCustomTemplate() {
    console.log('custom')
  }

  async downloadTemplate() {
    // 1. 通过项目模版api获取项目模版信息
    // 1.1 通过egg.js 搭建一套后端系统
    // 1.2 通过npm储存项目模板
    // 1.3 将项目模版存储到mongodb数据库中
    // 1.4 通过egg.js获取mongodb中的数据并且通过API返回
    const {templateName} = this.projectInfo
    const nowDownloadTemplate = this.template.find(item => item.npmName === templateName);

    const targetPath = path.resolve(userHome, '.imooc-env', 'template')
    const storePath = path.resolve(userHome, '.imooc-env', 'template', 'node_modules')
    const packageName = templateName;
    const packageNameVersion = nowDownloadTemplate.version;
    this.nowDownloadTemplateInfo = nowDownloadTemplate;

    const npmPkg = new Package({
      targetPath,
      storePath,
      packageName,
      packageNameVersion,
    })
    if(await npmPkg.exists()) {
      const Spinner = SpinnerFn('正在更新模版..')
      try {
        await npmPkg.update()
      } catch (error) {
        throw new Error(error.message)
      } finally {
        Spinner.stop(true)
        if(await npmPkg.exists()) {
          log.success('模版更新成功')
          this.templateNpm = npmPkg;
        }
      }
      // await Sleep(3000)
      
      // Spinner.stop(true)
    } else {
      const Spinner = SpinnerFn('正在下载模版..')
      // await Sleep(3000)
      try {
        await npmPkg.install()
      } catch (error) {
        throw new Error(error.message)
      } finally {
        Spinner.stop(true)
        if(await npmPkg.exists()) {
          log.success('模版下载成功')
          this.templateNpm = npmPkg;
        }
      }

    }
    // await new Promise((resolve) => setTimeout(() => {
    //   resolve()
    // }, 3000))



  }
  async prepare() {
    const templateArr = await handleGetTemplate()
    if(!templateArr || templateArr.length === 0) {
      throw new Error('项目模版不存在')
    }
    // 所有的模版
    this.template = templateArr;
    const nowPath = process.cwd();
    // 1. 判断文件是否为空
    if(!this.isCwdEmpty(nowPath)) {
      let ifContinue = false;
      if(!this.force) {
        let confirmRes = await inquirer.prompt([
          /* Pass your questions in here */{
          type: 'confirm',
          name: 'ifContinue',
          default: false,
          message: '当前文件夹不为空，是否继续创建项目？'}
        ])
        ifContinue = confirmRes.ifContinue
        if(!ifContinue) return
      }
      // --force = 强制安装 判断哪些文件即使存在，也不影响初始化
      // 2. 是否强制更新
      if(ifContinue || this.force) {
        const { confirmDelete } = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'confirmDelete',
            default: false,
            message: '是否确认清空当前文件夹？'
          }
        ])

        if(confirmDelete) {
          // 清空当前文件夹
          fse.emptyDirSync(nowPath)
        }
        // 不清空也要创建
      }

    }
    return this.getProjectInfo()
  }

  async getProjectInfo() {
    let projectInfo = {}
    // 3. 选择创建项目或者组件
    const { type } = await inquirer.prompt([
      {
        type: 'list',
        name: 'type',
        message: '请选择初始化类型',
        default: TYPE_PROJECT,
        choices: [
          {
            value:TYPE_PROJECT ,
            name: '项目'
          },
          {
            name: '组件',
            value: TYPE_COMPONENT
          }
        ]
      }
    ])
    if(type === TYPE_PROJECT) {
      const questionArr = [
         {
          type: 'input',
          name: 'projectVersion',
          message: '请输入项目版本号',
          default: '1.0.0',
          validate: function (v) {
            // Declare function as asynchronous, and save the done callback
            const done = this.async();
            // Do async stuff
            setTimeout(function() {
              if (!semver.valid(v)) {
                // Pass the return value in the done callback
                done('请输入合法的项目版本号');
              } else {
                // Pass the return value in the done callback
                done(null, true);
              }
            }, 1000);
          },
          filter: function (v) {
            if(semver.valid(v)) {
              return semver.valid(v)
            }
            return v
          }
        },{
          type: 'list',
          name: 'templateName',
          message: '请选择项目模版',
          choices: this.templateListSelect
        }
      ]
      if(!nameReg.test(this.projectName)) {
        questionArr.unshift({
          type: 'input',
          name: 'projectName',
          message: '请输入项目名称',
          default: '',
          validate: function(v) {
              // Declare function as asynchronous, and save the done callback
              const done = this.async();
              // Do async stuff
              setTimeout(function() {
                if (!nameReg.test(v)) {
                  // Pass the return value in the done callback
                  done('请输入合法的项目名称');
                } else {
                  // Pass the return value in the done callback
                  done(null, true);
                }
              }, 1000);
          },
          filter: function(v) {
            return v
          }
        },)
      } else {
        projectInfo.projectName = this.projectName;
      }
      const o = await inquirer.prompt(questionArr)
      projectInfo = {
        ...projectInfo,
        ...o
      }
    } else {

    }
    projectInfo.type = type;
    if(projectInfo.projectName) {
      projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '')
    }
    if(projectInfo.projectVersion) {
      projectInfo.version = projectInfo.projectVersion

    }
    // 4. 获取项目基本信息
    return projectInfo

  }

  get templateListSelect() {
    return this.template.map(item => {
      return {
        value: item.npmName,
        name: item.name
      }
    })
  }

  isCwdEmpty(nowPath) {
    // path.resolve('.')也可以拿到当前的目录
    // __dirname 拿到当前执行文件的目录
    let fileList = fs.readdirSync(nowPath);
    fileList = fileList.filter(item =>(
       !item.startsWith('.') && item !== 'node_modules'
    ))
    return !fileList || fileList.length === 0
  }
}

function init(argv) {
  return new InitCommand(argv)
}

module.exports = init;
module.exports.InitCommand = InitCommand;